package org.javahispano.javacup.tacticas_aceptadas.jpautom.org.javahispano.javacup.tacticas.pitufos;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.Collections;
import org.javahispano.javacup.modelo.*;


public class Util {
	public static final double EPSILON=0.000001;

	public static boolean tieneBola(Campo c, int j) {
		//TODO: considerar portero
		if (c.getBola().getPos().distance2d(c.getJugador(j).getPos())<=Util.getAlcanceH(c, j)
				&& c.getBola().getPos().getZ()<=Util.getAlcanceV(c, j)
				&& c.getJugador(j).getTSinRematar()==0)
			return true;
		return false;
	}

	public static boolean dentroCampo(Pos p) {
		if (Math.abs(p.x)<=Constantes.ANCHO_CAMPO_JUEGO/2 && Math.abs(p.y)<=Constantes.LARGO_CAMPO_JUEGO/2) return true;
		return false;
	}

	public static boolean isGol(Campo c) {
		//TODO: no es del todo correcto, hay una interpolacion entre dos iteraciones para determinar si entra por el arco
		//TODO: revisar todo, en general
		if (Math.abs(c.getBola().pos.x)<=Constantes.LARGO_ARCO/2
			&& Math.abs(c.getBola().pos.y)>Constantes.LARGO_CAMPO_JUEGO/2
			&& c.getBola().getPos().getZ()<Constantes.ALTO_ARCO) return true;
		return false;
	}

	public static void futuroBola(Campo.Bola b, int t) {
		for (int i=0; i<t; i++) {
			if (!dentroCampo(b.pos)) {
				b.mov.setVxyz(0.0, 0.0, 0.0);
				return;
			}
			if (b.pos.z==0 && b.mov.getVz()>=0 && b.mov.getVz()<3*Constantes.G) {
				// balon se mueve por el suelo
				b.pos.setLocation(b.pos.x+b.mov.getVx(), b.pos.y+b.mov.getVy(), 0);
				b.mov.setVx(b.mov.getVx()*Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO);
				b.mov.setVy(b.mov.getVy()*Constantes.FACTOR_DISMINUCION_VEL_BALON_SUELO);
				b.mov.setVz(0);
			}
			else {
				b.pos.setLocation(b.pos.x+b.mov.getVx(), b.pos.y+b.mov.getVy(), redondeaMultiplo(b.pos.z+b.mov.getVz(), Constantes.G));
				b.mov.setVx(b.mov.getVx()*Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE);
				b.mov.setVy(b.mov.getVy()*Constantes.FACTOR_DISMINUCION_VEL_BALON_AIRE);
				if (b.mov.getVz()<0 && b.pos.z<=0) {
					b.mov.setVz(redondeaMultiplo(-b.mov.getVz()*Constantes.FACTOR_DISMINUCION_ALTURA_BALON_REBOTE, Constantes.G));
				} else {
					b.mov.setVz(redondeaMultiplo(b.mov.getVz()-Constantes.G, Constantes.G));
				}

			}
		}
	}

	public static int intercepcion(Campo c, int j, Pos outLugar) {
		// Se calcula desde t=1
		int t=Math.max(1, c.getJugador(j).getTSinRematar());
		Campo.Bola b=c.getBola().getCopia();
		futuroBola(b, t);
		for (; t<200; t++) {
            if (b.getPos().getZ()>Util.getAlcanceV(c, j)) {
                futuroBola(b, 1);
                continue;
            }
			if (!dentroCampo(b.pos)) {
				if (outLugar!=null) outLugar.setLocation(b.pos);
				return (int)Math.ceil((c.getJugador(j).getPos().distance2d(b.getPos())-Util.getAlcanceH(c, j))/c.getInfo().getV(j));
			}
			//no es exacto el alcance del portero
			if (c.getJugador(j).getPos().distance2d(b.getPos())<=t*c.getInfo().getV(j)+Util.getAlcanceH(c, j, b.getPos())) {
				if (outLugar!=null) outLugar.setLocation(b.pos);
				return t;
			}
			futuroBola(b, 1);
		}
		return t;
	}


	public static int[] intercepcionMultiple(Campo c, Pos[] outLugar, boolean simple) {
		// Se calcula desde t=1
		Campo.Bola b=c.getBola().getCopia();
		int[] tInter=new int[22];
		int restan=22;

		for (int t=1; restan>0 && t<1000; t++) {
			futuroBola(b, 1);

			if (!dentroCampo(b.pos)) {
				for (int j=0; j<22; j++) {
					if (tInter[j]>0 || c.getJugador(j).getTSinRematar()>t) continue;
					tInter[j]=(int)Math.ceil((c.getJugador(j).getPos().distance2d(b.getPos())-Util.getAlcanceH(c, j, b.pos))/c.getInfo().getV(j));
					if (outLugar!=null) outLugar[j]=(Pos)(b.pos.clone());
					restan--;
				}
				continue;
			}
			
			for (int j=0; j<22; j++) {
				if (tInter[j]!=0 || c.getJugador(j).getTSinRematar()>t) continue;
				if (c.getJugador(j).pos.distance2d(b.pos)<=t*c.getInfo().getV(j)+Util.getAlcanceH(c, j, b.pos)) {
					tInter[j]=t;
					if (outLugar!=null) outLugar[j]=(Pos)(b.pos.clone());
					restan--;
				}
			}
			if (simple && restan<22) return tInter;
		}
		return tInter;
	}


	public static double getAlcanceH(Campo c, int j) {
		return getAlcanceH(c, j, c.getJugador(j).getPos());
	}

	public static double getAlcanceH(Campo c, int j, Pos p) {
		if (!c.getInfo().isPortero(j)) return Constantes.DISTANCIA_CONTROL_BALON;
		if ((j/11==0 && isDentroArea1(p))
				|| (j/11==1 && isDentroArea2(p)))
			return Constantes.DISTANCIA_CONTROL_BALON_PORTERO;
		return Constantes.DISTANCIA_CONTROL_BALON;
	}

	public static double getAlcanceV(Campo c, int j) {
		return getAlcanceV(c, j, c.getJugador(j).getPos());
	}

	public static double getAlcanceV(Campo c, int j, Pos p) {
		if ((j/11==0 && isDentroArea1(p))
				|| (j/11==1 && isDentroArea2(p)))
			return Constantes.ALTO_ARCO;
		return Constantes.ALTURA_CONTROL_BALON;
	}

	public static boolean isDentroArea1(Pos p) {
		if (Math.abs(p.x)<=Constantes.LARGO_AREA_GRANDE/2 && p.y<-Constantes.LARGO_CAMPO_JUEGO/2+Constantes.ANCHO_AREA_GRANDE)
			return true;
		return false;
	}

	public static boolean isDentroArea2(Pos p) {
		if (Math.abs(p.x)<=Constantes.LARGO_AREA_GRANDE/2 && p.y<Constantes.LARGO_CAMPO_JUEGO/2-Constantes.ANCHO_AREA_GRANDE)
			return true;
		return false;
	}


	public static Pos[] matchPriority(Campo c, int[] jugs, Pos[] poss) {
		double[][] dist=new double[jugs.length][poss.length];
		for (int i=0; i<jugs.length; i++) {
			for (int j=0; j<poss.length; j++) {
				dist[i][j]=c.getJugador(jugs[i]).getPos().distance2d(poss[j]);
			}
		}
		int nmatch=Math.min(jugs.length, poss.length);
		Pos[] pres=new Pos[jugs.length];
		boolean[] usado=new boolean[jugs.length];

		for (int j=0; j<nmatch; j++) {
			double d=Double.MAX_VALUE;
			int mini=0;
			for (int i=0; i<jugs.length; i++) {
				if (!usado[i] && dist[i][j]<d) {
					d=dist[i][j];
					mini=i;
				}
			}
			usado[mini]=true;
			pres[mini]=poss[j];
		}
		return pres;
	}


	public static Pos[] match(Campo c, int[] jugs, Pos[] poss) {
		double[][] dist=new double[jugs.length][poss.length];
		for (int i=0; i<jugs.length; i++) {
			for (int j=0; j<poss.length; j++) {
				dist[i][j]=c.getJugador(jugs[i]).getPos().distance(poss[j]);
			}
		}

		class Pair implements Comparable {
			int j1, j2;
			double dist;

			public int compareTo(Object p) {
				if (dist<((Pair)p).dist) return -1;
				else if (dist==((Pair)p).dist) return 0;
				return 1;
			}
		}

		ArrayList<Pair> distancias=new ArrayList();
		boolean[] usado=new boolean[11];

		for (int i=0; i<jugs.length; i++) {
			for (int j=0; j<poss.length; j++) {
				Pair p=new Pair();
				p.j1=i; p.j2=j; 
				p.dist=c.getJugador(jugs[i]).getPos().distance(poss[j]);
				distancias.add(p);
			}
		}

		Collections.sort(distancias);
		boolean[] used1=new boolean[jugs.length];
		boolean[] used2=new boolean[poss.length];
		Pos[] pres=new Pos[jugs.length];
		for (int i=0; i<distancias.size(); i++) {
			int j1=distancias.get(i).j1;
			int j2=distancias.get(i).j2;
			if (!used1[j1] && !used2[j2]) {
				used1[j1]=true;
				used2[j2]=true;
				pres[j1]=poss[j2];
			}
		}
		return pres;
	}



	public static Pos getPosDesplazadaDefensa(Pos p, double d) {
		Pos port=new Pos(0, -Constantes.LARGO_CAMPO_JUEGO/2, 0);
		double ang=Util.angulo(p, port);
		return new Pos(p.x+d*Math.cos(ang), p.y+d*Math.sin(ang), p.z);

	}




	public static ComandoIrA crearComandoIrA(Campo c, int j, Pos p) {
		return new ComandoIrA(j, new Posicion(p.getX(), p.getY()));
	}

	public static ComandoGolpearBalon crearComandoGolpearBalon(Campo c, int j, double fz, double th, double phi) {
		return new ComandoGolpearBalon(j, phi*180/Math.PI, fz/c.getInfo().getF(j), th*180/Math.PI);
	}

	public static ComandoGolpearBalon crearComandoGolpearBalonZ(Campo c, int j, double vxy, double vz, double phi) {
		double th=Math.asin(vz/vxy);
		double fz=(vxy/Math.cos(th));
		//Util.log("Conversion:   vxy=%.4f vz=%.4f phi=%.4f\n", vxy, vz, phi);
		//Util.log("Comprobacion: vxy=%.4f vz=%.4f phi=%.4f\n", fz*Math.cos(th), fz*Math.cos(th)*Math.sin(th), phi);


		return new ComandoGolpearBalon(j, phi*180/Math.PI, (vxy/Math.cos(th))/c.getInfo().getF(j), th*180/Math.PI);
	}

	public static void log(String cad, Object... args) {
		boolean bLog=false;
		if (bLog)
			System.out.printf(cad, args);
	}


	public static double angulo(Pos p1, Pos p2) {
		return Math.atan2(p2.y-p1.y, p2.x-p1.x);
	}

	// devuelve angulo [0, 2*PI)
	public static double normang(double a) {
		if (a>=0) return a%(2*Math.PI);
		else return 2*Math.PI+a%(2*Math.PI);
	}

	public static double redondeaMultiplo(double valor, double divisor) {
		return Math.round(valor / divisor) * divisor;
	}

	// interseccion de c1 con c2 
	// (si c1 contiene c2, la interseccion es completa)
	// (si c2 contiene c1, no hay interseccion)
	double[] interseccionCircunferencias(Pos p1, double r1, Pos p2, double r2) {
		double d=p1.distance(p2);
		if (d>r1+r2 || r2>d+r1) {
			return null;
		}
		else if (r1>d+r2) {
			return new double[]{EPSILON, 2*Math.PI};
		}
		double a=Math.acos((d*d+r1*r1-r2*r2)/(2*r1*d));
		double b=Math.atan2(p2.y-p1.y, p2.x-p1.x);
		return new double[]{normang(b-a), normang(b+a)};
	}
}
